module Explode.GameState;
private import Explode.GameTitleState;
private import Explode.GameoverState;
private import Explode.PauseMenuState;
private import Explode.Stage;
private import Explode.Player;
private import Explode.GameConfig;
private import State.IState;
private import Input.KeyCode;
private import Input.GamePad;
private import Input.Mouse;
private import Input.InputRecorder;
private import Graphics.IGraphics;
private import Sound.Manager;
private import System.Console;
private import std.file;
private import std.string;


/**
*	ステージ開始DEMO用
*/
private class StageStartState : BasicState {
	private GameStage _stage;
	private GameParam _param;
	private IState _caller;
	private Player _player;
	private int _frameCnt;
	protected IGamePad _gamepad;
	protected ISoundSprite _bgm;
	public this( GameStage stage , GameParam param , IGamePad pad){
		_stage  = stage;
		_param  = param;
		_gamepad = pad;
	} //
	public IState call( IState caller ) {
		_caller = caller;
		_player = _stage.player;
		for ( int i = 0 ; i < 90 ; i++ ) {
			_stage.zoomIn();
		}
		_bgm = SoundManager.background;
		_bgm.play( GameConfig.startBgm , 0 );
		_frameCnt = 0;
		return this;
	} //

	public IState action() {
		if ( _frameCnt > 20 ){
			_stage.zoomOut();
			_stage.zoomOut();
			_stage.zoomOut();
			_stage.zoomOut();
		}
		_frameCnt++;
		if ( _frameCnt < 45 || (_bgm.busy && _gamepad.getButton( 0 ) == 0 && _gamepad.getButton( 1 ) == 0 && _gamepad.x == 0 && _gamepad.y == 0) ) {
			return this;
		} else {
			return _caller.call( this );
		}
	} //

	public IState draw( IGraphics g ) {
		g.setViewport( 0 , 0, 640 , 480 );
		Color c = Color.create( 0x60000000u);
		g.drawFilledRect( 0 , 0 , 640 , 480 , c );
		_stage.setViewport( g );
		_stage.drawingEntry( g );
		g.draw();
		return this;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_gamepad.y = -1;
				break;
			case KeyCode.DOWN:
				_gamepad.y =  1;
				break;
			case KeyCode.LEFT:
				_gamepad.x = -1;
				break;
			case KeyCode.RIGHT:
				_gamepad.x =  1;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 1 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_gamepad.y = 0;
				break;
			case KeyCode.DOWN:
				_gamepad.y = 0;
				break;
			case KeyCode.LEFT:
				_gamepad.x = 0;
				break;
			case KeyCode.RIGHT:
				_gamepad.x = 0;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 0 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _gamepad.x = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _gamepad.y = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		_gamepad.setButton( GameConfig.getButtonMap( button ) , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_gamepad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //
	

} //



/**
*	ステージクリアDEMO用
*/
private class StageClearState : BasicState {
	private GameStage _stage;
	private GameParam _param;
	private IState _caller;
	private Player _player;
	private int _frameCnt;
	protected ISoundSprite _snd;
	protected IGamePad _gamepad;
	protected bool _autoDemo = false;
	public bool autoDemo() {
		return _autoDemo;
	} //
	public void autoDemo( bool v ) {
		_autoDemo = v;
	} //
	public this( GameStage stage , GameParam param , IGamePad pad){
		_stage  = stage;
		_param  = param;
		_gamepad = pad;
	} //
	public IState call( IState caller ) {
		_caller = caller;
		_player = _stage.player;
		_param.score 	= _param.score + _player.score + GameConfig.SCORE_STAGECLEAR;
		_param.frameCnt = _param.frameCnt + _stage.frameCnt;
		if ( _autoDemo )
			_snd = SoundManager.getSprite();
		else
			_snd = SoundManager.background();
		_snd.play( _stage.goalSound , 0 ).release();
		_frameCnt = 0;
		return this;
	} //

	public IState action() {
		_stage.zoomIn();
		_frameCnt++;
		if ( _frameCnt < 60 ) {
			return this;
		} else {
			if ( _autoDemo || !_snd.busy 
				||  _gamepad.getButton( 0 ) != 0 || _gamepad.getButton( 1 ) != 0
				) {
				_snd.release();
				return _caller.call( this );
			} else
				return this;
		}
	} //

	public IState draw( IGraphics g ) {
		g.setViewport( 0 , 0, 640 , 480 );
		Color c = Color.create( 0x60000000u);
		g.drawFilledRect( 0 , 0 , 640 , 480 , c );
		_stage.setViewport( g );
		_stage.drawingEntry( g );
		g.draw();
		return this;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_gamepad.y = -1;
				break;
			case KeyCode.DOWN:
				_gamepad.y =  1;
				break;
			case KeyCode.LEFT:
				_gamepad.x = -1;
				break;
			case KeyCode.RIGHT:
				_gamepad.x =  1;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 1 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_gamepad.y = 0;
				break;
			case KeyCode.DOWN:
				_gamepad.y = 0;
				break;
			case KeyCode.LEFT:
				_gamepad.x = 0;
				break;
			case KeyCode.RIGHT:
				_gamepad.x = 0;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 0 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _gamepad.x = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _gamepad.y = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		_gamepad.setButton( GameConfig.getButtonMap( button ) , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_gamepad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //
	

} //

/**
*	死亡DEMO用
*/
private class DyingState : BasicState {
	private BaseStage _stage;
	private GameParam _param;
	private IState _caller;
	private Player _player;
	private int _frameCnt;
	public this( BaseStage stage , GameParam param){
		_stage  = stage;
		_param  = param;
		_player = stage.player;
	} //
	public IState call( IState caller ) {
		_caller = caller;
		_player = _stage.player;
//		_param.score 	= _param.score + _player.score;
		_frameCnt = 0;
		SoundManager.background.stop();
		return this;
	} //

	public IState action() {
		if ( _frameCnt > 20 ){
			_stage.zoomIn();
			_stage.zoomIn();
			_stage.zoomIn();
			_stage.zoomIn();
		}
		_frameCnt++;
		_player.action();
		if ( _frameCnt < 60 ) {
			return this;
		} else {
			return _caller.call( this );
		}
	} //

	public IState draw( IGraphics g ) {
		g.setViewport( 0 , 0, 640 , 480 );
		Color c = Color.create( 0x30ff0000u);
		g.drawFilledRect( 0 , 0 , 640 , 480 , c );
		_stage.setViewport( g );
		_stage.drawingEntry( g );
		g.draw();
		return this;
	} //

} //



/**
*	メインゲーム処理用
**/
public class GameState : BasicState{
	protected IGraphics graphics;
	protected IGamePad _gamepad;
	protected BasicMouse _mouse;
	protected int _frameCnt = 0;
	protected GameStage _stage;
	protected IGraphics _graphics;
	protected int _zoom = 0;
	protected int _key_dn = 0 , _key_up = 0 , _key_lt = 0 , _key_rt = 0;
	
	protected GameParam _param;

	protected IState _stageStart , _dying , _stageClear;
	protected GameTitleState _gameTitle;
	protected GameoverState _gameover;
	protected PauseMenuState _pausemenu;
	
	protected int _giveupCnt = 0;
	protected bool _pauseFlag;

	public GameStage stage(){ return _stage;}

	public this( IGraphics g ){
		_graphics = g;
	} //

	public IState action(){
		_frameCnt++;
		_stage.action();
			 if ( _zoom > 0 ) _stage.zoomIn();
		else if ( _zoom < 0 ) _stage.zoomOut();


		if ( _pauseFlag ) return _pausemenu.call( this );
		if ( _stage.mode == GameStage.MODE_DEAD ) {
			ISoundSprite s = SoundManager.getSprite();
			s.play( _stage.missSound ).release();
			return _dying.call( this );
		} else 
		if ( _stage.mode == GameStage.MODE_CLEAR ) {
			ISoundSprite s = SoundManager.getSprite();
			s.release();
			return _stageClear.call( this );
		}
		
		if ( _gamepad.getButton( 0 )  > 0 && _gamepad.getButton( 1 ) > 0 ) {
			_giveupCnt++;
			if ( _giveupCnt > 180 ) _stage.setGiveup();
		} else
			_giveupCnt = 0;

		return this;
	} //

	public IState draw( IGraphics g ) {
		g.clear();
			_stage.setViewport( g );
			_stage.drawingEntry( g );
		g.draw();
		return this;
	} //

	public IState call( IState caller ){
		initObject();

		_zoom   = 0;
		_key_dn = 0;
		_key_up = 0;
		_key_lt = 0;
		_key_rt = 0;
		_pauseFlag = false;
		if ( !(caller is null || caller is _pausemenu) ) {
			SoundManager.background.play( _stage.gameBgm , -1 );
		}

		return nextCall( caller );
	} //
	
	protected void initObject(){
		if ( _stage is null ) {
			_gamepad	   = new VirtualGamePad( 8 );
			_mouse		   = new BasicMouse();
			_param		   = new GameParam();
			_stage		   = new GameStage( _graphics , _param );
			_stage.gamepad = _gamepad;
			_stage.mouse   = _mouse;
			_gameTitle     = new GameTitleState( this , _param , _gamepad , _graphics );
			_gameover      = new GameoverState(  this , _param , _gamepad );
			_stageStart    = new StageStartState( _stage , _param , _gamepad );
			_pausemenu     = new PauseMenuState( this , _param , _gamepad , _graphics );
			_stageClear	   = new StageClearState( _stage , _param , _gamepad );
			_dying		   = new DyingState( _stage , _param );
			setStageNumber( GameConfig.lastStage );
		}
	} //
	
	protected IState nextCall( IState caller ) {
		if ( caller is null ) {
			return _gameTitle.call( this );
		} else
		if ( caller is _gameTitle ) {
			_param.reset();
			_param.stageNum = setStageNumber( _gameTitle.selectedValue );
			return _stageStart.call( this );
		} else
		if ( caller is _gameover ) {
			if ( _gameover.nextFlag == GameoverState.GAME_CONTINUE ) {
				int s = _param.stageNum;
				_param.reset();
				_param.stageNum = setStageNumber( s );
				return _stageStart.call( this );
			} else {
				return _gameTitle.call( this ) ;
			}
		} else
		if ( caller is _pausemenu ) {
			_gamepad.reset();
			switch ( _pausemenu.returnValue ) {
				case PauseMenuState.STAGESELECT:
					_param.stageNum = setStageNumber( _pausemenu.selectedValue );
					return _stageStart.call( this );
				case PauseMenuState.TITLE:
					return _gameTitle.call( this );
				case PauseMenuState.GIVEUP:
					_stage.setGiveup();
				default:
					SoundManager.background.resume();
					return this;
			}
		} else
		if ( caller is _stageClear ) {
			saveReplay();
			_param.stageNum = setStageNumber( _param.stageNum + 1 );
			return _stageStart.call( this );
		} else
		if ( caller is _dying ) {
			_param.life--;
			if ( _param.life < 0 ) {
				_param.life = 0;
				return _gameover.call( this ) ;
			} else {
				_param.stageNum = setStageNumber( _param.stageNum );
				return _stageStart.call( this );
			}
		}
		return this;
	} //


	public int setStageNumber( int num ) {
		string file   = format( "stage%04d.txt" , num );
		string src	  = GameConfig.stageDir ~ "/" ~ file;
		string replay = GameConfig.stageDir ~ "/replay/" ~ file;
		string defSrc = GameConfig.stageDir ~ "/stage0001.txt";
		_param.resetStageParam();
		if ( !exists( src ) ) {
			src = defSrc;
			num = 1;
			if( !exists( src ) ) {
				return false;
			}
		}
		Console.write("Loading stage \"%s\"..." , src );
		_stage.loadStageFromFile( src );
		Console.writeln("OK");
		if ( exists( replay ) ) {
			string  head = split( cast(string)read( replay ) , "[body]" )[0];
			string[] dat = split( head , ",");
			_param.bestTime = cast(int)atoi( dat[0] );
		}
		return num;
	} //

	protected bool saveReplay() {
		string code = _stage.replayCode;
		if ( code is null ) return false;
		int frameCnt = _stage.frameCnt;
		string dir = GameConfig.stageDir ~ "/replay/";
		string file = dir ~= format( "stage%04d.txt" , _param.stageNum );
		if ( exists( file ) ) {
			string content = cast(string)read( file );
			string chk = split( content , "[next]" )[0];
			if ( chk.length > 1 ) {
				int frm	   = cast(int)atoi( split( chk , "," )[0] );
				Console.writeln("BEST TIME=%d CURRENT TIME=%d" , frm , frameCnt );
				if ( frm <= frameCnt ) return false;
			}
			remove( file );
		}
		Console.writeln("replaycode:" ~ code);
		write( file , code );
		return true;
	} //



	public bool onMouseMove( int x , int y , int button){
		_mouse.x = x;
		_mouse.y = y;
		return true;
	} //
	public bool onMouseDown( int x , int y , int button){
		_mouse.button[ button ] = true;
		return true;
	} //
	public bool onMouseUp( int x , int y , int button){
		_mouse.button[ button ] = false;
		return true;
	} //
	public bool onMouseWheel( int x , int y , int delta){
//		if ( delta < 0 ) _zoom =  1;
//		 else 			 _zoom = -1;
		if ( delta < 0 ) _mouse.delta =  1;
		else			 _mouse.delta = -1;
		return true;
	}

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_key_up = -1;
				_gamepad.y = -1;
				break;
			case KeyCode.DOWN:
				_key_dn =  1;
				_gamepad.y =  1;
				break;
			case KeyCode.LEFT:
				_key_lt = -1;
				_gamepad.x = -1;
				break;
			case KeyCode.RIGHT:
				_key_rt =  1;
				_gamepad.x =  1;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 1 );
				break;
			case KeyCode.PAGEDOWN:
				_zoom = -1;
				break;
			case KeyCode.PAGEUP:
				_zoom =  1;
				break;
			case KeyCode.HOME:
				_zoom =  0;
				break;
			case KeyCode.SPACE:
				_pauseFlag = true;
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_key_up = 0;
				_gamepad.y = _key_dn;
				break;
			case KeyCode.DOWN:
				_key_dn = 0;
				_gamepad.y = _key_up;
				break;
			case KeyCode.LEFT:
				_key_lt = 0;
				_gamepad.x = _key_rt;
				break;
			case KeyCode.RIGHT:
				_key_rt = 0;
				_gamepad.x = _key_lt;
				break;
			case KeyCode.Z:
				_gamepad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_gamepad.setButton( 1 , 0 );
				break;
			case KeyCode.PAGEDOWN:
				_zoom = 0;
				break;
			case KeyCode.PAGEUP:
				_zoom =  0;
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _gamepad.x = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _gamepad.y = ( delta > 0 ) ? 1 : -1;
			else			 _gamepad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		int bm = GameConfig.getButtonMap( button );
		if ( bm == 9 ) _pauseFlag = true;
		else
		if ( bm == 8 ) _stage.setGiveup();
		else
			_gamepad.setButton( bm , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_gamepad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //
	
	
	
} //